import difflib
import io
import os
import re
import subprocess
import tempfile
import time
import paramiko

from threading import Lock, Thread
from modules.logs import Logs


class _Buffer:
    def __init__(self):
        self._io = io.StringIO()
        self._lock = Lock()

    def write(self, data):
        try:
            self._lock.acquire()
            self._io.write(data)
        finally:
            self._lock.release()

    def getvalue(self):
        return self._io.getvalue()

    def close(self):
        self._io.close()


class Base:
    def __init__(self):
        self.log = Logs(self.__class__.__name__)
        self.buffer_size = 1024 * 1024

    @staticmethod
    def _read_to(stream, buffer):
        def _read_handle():
            buffer.write(stream)

        thread = Thread(target=_read_handle)
        thread.start()
        return thread

    def sh(self, cmd, ispwd=False, is_print=True):
        if not ispwd:
            self.log.info(f'开始执行: {cmd.strip()}')
        f_out = None
        tem_f = tempfile.TemporaryFile()
        try:
            sub_obj = subprocess.Popen(cmd,
                                       shell=True,
                                       encoding="utf-8",
                                       stdout=tem_f,
                                       stderr=tem_f)
            sub_obj.wait()
            tem_f.seek(0)
            f_out = tem_f.read().decode('utf-8')
            if is_print:
                self.log.info(f'Success: {f_out}')
        except Exception as e:
            self.log.error(f'Error: {str(e)}')
        finally:
            tem_f.close()
        return f_out.strip()

    @staticmethod
    def ssh_connect(ip, username, pwd):
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(ip, 22, username, pwd, timeout=600, banner_timeout=60)
        return ssh

    def ssh_run(self, ssh, cmd, ispwd=False, is_print=True):
        if not ispwd:
            self.log.info(f'执行命令: {cmd.strip()}')
        channel = ssh.get_transport().open_session()
        channel.exec_command(cmd)
        buffer = _Buffer()
        cnt = 0
        return_res = None
        try:
            while True:
                if cnt >= 12000:
                    self.log.info(f'ssh_run执行超时')
                    break
                while channel.recv_ready():
                    stdout = channel.recv(self.buffer_size)
                    stdout_reader = self._read_to(stdout.decode('utf-8'),
                                                  buffer)
                    stdout_reader.join()
                while channel.recv_stderr_ready():
                    stderr = channel.recv_stderr(self.buffer_size)
                    stderr_reader = self._read_to(stderr.decode('utf-8'),
                                                  buffer)
                    stderr_reader.join()
                if channel.exit_status_ready():
                    if is_print:
                        self.log.info(f'执行完成')
                    break
                time.sleep(0.1)
                cnt += 1
            return_res = buffer.getvalue()
            if is_print:
                self.log.info(f'{return_res}\n')
        except Exception as e:
            self.log.error(f'Error: {str(e)}')
        finally:
            buffer.close()
        return return_res.strip()

    def ssh_run_sql(self, ssh, sql, db=None, user=None, **kwargs):
        """
        执行sql
        :param ssh: ssh连接
        :param sql: sql语句
        :param db: 数据库名，不传时默认取db_name
        :param user: 用户名，默认None
        :param kwargs: 关键字参数
        :return: 执行结果
        """
        run_db = kwargs["db_name"] if db is None else db
        cmd = f'source {kwargs["db_env"]};' \
              f'gsql -d {run_db} -p {kwargs["db_port"]} -c "{sql}" '
        if user:
            cmd += f'-U {user} -W {kwargs["db_pwd"]}'
        self.log.info(cmd)
        res = self.ssh_run(ssh, cmd, True, False)
        return res

    @staticmethod
    def ssh_close(ssh):
        ssh.close()

    def part_file(self, f_path, size=2097152):
        """文件大于size指定大小时，创建新的文件，并返回新文件名"""
        os.makedirs(os.path.dirname(f_path), exist_ok=True)
        if os.path.exists(f_path):
            f_size = self.sh(
                f"ls -l {f_path}|awk '{{print $5}}'", True, False)
            i = 0
            while int(f_size.strip()) >= size:
                i += 1
                f_name = os.path.basename(f_path)
                num = re.search(r'_(\d*)\.', f_name, re.S)
                if num and len(num.group(1).strip()) > 0:
                    next_num = int(num.group(1)) + 1
                    new_name = f_name.replace(num.group(1), str(next_num))
                else:
                    new_name = f_name.replace('.html', f'_1.html')
                f_path = f_path.replace(f_name, new_name)
                if os.path.exists(f_path):
                    f_size = self.sh(
                        f"ls -l {f_path}|awk '{{print $5}}'", True, False)
                else:
                    break
        return f_path

    def compare_text(self, lable, source, target, f_path):
        new_path = self.part_file(f_path)
        source_list = source.splitlines()
        target_list = target.splitlines()
        old_str = 'charset=ISO-8859-1'
        new_str = 'charset=UTF-8'
        s_len = len(source_list)
        t_len = len(target_list)
        max_length = s_len if s_len > t_len else t_len
        start_pos = 0
        step = 50
        end_pos = start_pos + step
        diff = difflib.HtmlDiff()
        detail_info = ''
        while end_pos < max_length + step:
            detail_info += diff.make_file(source_list[start_pos: end_pos],
                                          target_list[start_pos: end_pos],
                                          lable, lable)
            start_pos = end_pos
            end_pos = start_pos + step
        with open(new_path, 'a+', encoding='utf-8') as f_new:
            f_new.write(detail_info.replace(old_str, new_str))

    @staticmethod
    def deal_with_diffhtml(f_path):
        """处理对比结果html文件，只保留一个图例"""
        pattern = r'<table class="diff" summary="Legends">\s+' \
                  r'<tr> <th colspan="2"> Legends </th> </tr>\s+' \
                  r'<tr> <td> <table border="" summary="Colors">\s+' \
                  r'<tr><th> Colors </th> </tr>\s+' \
                  r'<tr><td class="diff_add">&nbsp;Added&nbsp;</td></tr>\s+' \
                  r'<tr><td class="diff_chg">Changed</td> </tr>\s+' \
                  r'<tr><td class="diff_sub">Deleted</td> </tr>\s+' \
                  r'</table></td>\s+' \
                  r'<td> <table border="" summary="Links">\s+' \
                  r'<tr><th colspan="2"> Links </th> </tr>\s+' \
                  r'<tr><td>\(f\)irst change</td> </tr>\s+' \
                  r'<tr><td>\(n\)ext change</td> </tr>\s+' \
                  r'<tr><td>\(t\)op</td> </tr>\s+' \
                  r'</table></td> </tr>\s+' \
                  r'</table>'
        with open(f_path, 'r', encoding='utf-8', errors='ignore') as rf:
            contant = rf.read()
        regex = re.findall(pattern, contant)
        new_contant = re.sub(pattern, '<br><br>', contant, len(regex) - 1)
        with open(f_path, 'w+', encoding='utf-8', errors='ignore') as wf:
            wf.write(new_contant)
